﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EuroManager.Domain.Simulator.Engine;
using EuroManager.Domain.Simulator.Engine.Movements;
using EuroManager.Domain.Simulator.Helpers;
using NUnit.Framework;
using Rhino.Mocks;

namespace EuroManager.Domain.Simulator.Tests.Engine
{
    [TestFixture]
    public class SteeringTests
    {
        private Steering steering;

        [SetUp]
        public void SetUp()
        {
            steering = new Steering(new Vector2D(3, 8), 3, 2, 10);
        }

        [Test]
        public void ShouldStopOnStart()
        {
            Assert.IsInstanceOf<StillMovement>(steering.Movement);
        }

        [Test]
        public void ShouldExposeSpeed()
        {
            steering.Hit(new Vector2D(5, 3));
            TestHelper.Advance(1, steering);

            Assert.AreEqual(steering.Velocity.Length, steering.Speed, Maths.Epsilon);
        }

        [Test]
        public void ShouldUtilizeMovement()
        {
            IMovement movement = MockRepository.GenerateStub<IMovement>();
            steering.Move(movement);

            Assert.AreSame(movement, steering.Movement);
        }

        [Test]
        public void ShouldUpdateMovement()
        {
            float time = 0.2f;
            IMovement movement = MockRepository.GenerateMock<IMovement>();
            movement.Expect(m => m.Update(time));

            steering.Move(movement);
            steering.Update(time);

            movement.VerifyAllExpectations();
        }

        [Test]
        public void ShouldStopWhenMovementFinished()
        {
            IMovement movement = MockRepository.GenerateStub<IMovement>();
            movement.Stub(m => m.IsFinished).Return(true);
            
            steering.Move(movement);
            steering.Update(0.1f);

            Assert.IsInstanceOf<StillMovement>(steering.Movement);
        }

        [Test]
        public void ShouldAdvanceLocation()
        {
            Vector2D location = new Vector2D(5, 8);

            IMovement movement = MockRepository.GenerateStub<IMovement>();
            movement.Stub(m => m.Location).Return(location);

            steering.Move(movement);
            steering.Update(0.1f);

            Assert.AreEqual(location, steering.Location);
        }

        [Test]
        public void ShouldAdvanceVelocity()
        {
            Vector2D velocity = new Vector2D(-3, 2);
            IMovement movement = MockRepository.GenerateStub<IMovement>();
            movement.Stub(m => m.Velocity).Return(velocity);

            steering.Move(movement);
            steering.Update(0.1f);

            Assert.AreEqual(velocity, steering.Velocity);
        }

        [Test]
        public void ShouldStopWhenRequested()
        {
            Vector2D velocity = new Vector2D(3, -2);
            steering.Hit(velocity);

            steering.Update(0.1f);
            steering.Stop();
            steering.Update(0.1f);

            Assert.IsInstanceOf<StillMovement>(steering.Movement);
            Assert.AreNotEqual(Vector2D.Zero, steering.Velocity);
        }

        [Test]
        public void ShouldHaltWhenRequested()
        {
            Vector2D velocity = new Vector2D(3, -2);
            steering.Hit(velocity);

            steering.Update(0.1f);
            steering.Halt();
            steering.Update(0.1f);

            Assert.IsInstanceOf<StillMovement>(steering.Movement);
            Assert.AreEqual(Vector2D.Zero, steering.Velocity);
        }

        [Test]
        public void ShouldMeetWhenRequested()
        {
            steering.Meet(new Vector2D(5, 3), 1, 5);
            steering.Update(0.1f);

            Assert.IsInstanceOf<ArriveMovement>(steering.Movement);
        }

        [Test]
        public void ShouldArriveWhenRequested()
        {
            steering.Arrive(new Vector2D(5, 3), 1);
            steering.Update(0.1f);

            Assert.IsInstanceOf<ArriveMovement>(steering.Movement);
        }

        [Test]
        public void ShouldTurnAtTargetWhenNotMoving()
        {
            Vector2D target = new Vector2D(15, -8);
            steering.Turn(target);

            TestHelper.Advance(10, steering);

            Assert.IsTrue(steering.Heading.IsParallelWithSense(target - steering.Location));
        }

        [Test]
        public void ShouldTurnToFaceDestinationWhenMoving()
        {
            Vector2D target = new Vector2D(-120, 540);
            steering.Arrive(target, 1);

            TestHelper.Advance(20, steering);

            Assert.IsTrue(steering.Heading.IsParallelWithSense(steering.Velocity));
        }
    }
}
